CBR-MP3Amp 0.6e – Notiz zur Größenoptimierung
================================================

Ausgangsprojekt:
MiniAmp 0.6e

Finaler Name:
CBR-MP3Amp 0.6e

Assembler:
Flat Assembler (FASM 1), PE64 / Win64

Wiedergabe:
Windows MCI / mciSendStringW

Ausgangsgröße:
26.624 Bytes

Endgröße:
10.752 Bytes

Ersparnis:
15.872 Bytes
ca. 59.6 % kleiner

Finale getestete Version:
CBR-MP3Amp_0.6e_size_optimized_v3_1.asm

Hinweis:
Alle hier genannten Endgrößen stammen aus den lokal mit FASM erzeugten und
getesteten EXE-Dateien.


1. V1 – Null-Daten konsequent nach .bss
----------------------------------------

Der mit Abstand größte Größengewinn kam durch die Trennung von initialisierten
und uninitialisierten Daten.

Im ursprünglichen Build lagen zahlreiche nur mit Null initialisierte
Laufzeitdaten in .data. Dazu gehörten unter anderem:

  - Fenster- und Control-Handles
  - Playlist-/MCI-Zustand
  - Scroll-/Seek-Zwischenwerte
  - WNDCLASSW
  - MSG
  - WIN32_FIND_DATAW
  - GDI-Arbeitsdaten
  - große Unicode-Arbeitspuffer

Beispiele der großen Puffer:

  exeDir
  searchPath
  fileName
  fullPath
  commandBuffer
  errorBuffer
  statusBuffer
  timeBuffer
  volumeTextBuffer

Diese Daten benötigen beim Programmstart keinen gespeicherten Inhalt.
Sie wurden deshalb in:

  section '.bss' data readable writeable

verschoben. Windows stellt den Speicher beim Laden bereits nullinitialisiert
bereit, sodass die vielen Nullbytes nicht mehr in der EXE gespeichert werden.

In .data blieben zunächst nur Werte, die einen echten Startwert benötigen,
beispielsweise:

  hFind = INVALID_HANDLE_VALUE
  volumePercent = 30
  newVolume = 30

Zusätzliche V1-Bereinigungen:

  - unbenutzten GetLastError-Aufruf im Startfehlerpfad entfernt
  - lastError entfernt
  - doppelten String "Pause" zusammengelegt
  - unbenutzte GDI-Laufzeitvariablen entfernt
  - unbenutzte Imports entfernt:
      GetLastError
      GetSysColor
      CreatePen
      Rectangle

Ergebnis V1:
12.800 Bytes

Gewinn gegenüber Original:
13.824 Bytes


2. V2 – GUI-Erzeugung tabellengesteuert
----------------------------------------

OnCreate enthielt elf sehr ähnliche CreateWindowExW-Sequenzen für:

  - Titelfeld
  - Zeitfeld
  - Positionsregler
  - Zurück
  - Play
  - Pause
  - Stop
  - Weiter
  - Lautstärkeanzeige
  - Lautstärkeregler
  - Playlist

Jeder Block enthielt wiederholt:

  CreateWindowExW
  Speichern des HWND
  Test auf Fehler
  optional WM_SETFONT

Diese Wiederholungen wurden durch eine gemeinsame Routine ersetzt:

  CreateGuiControls

Die elf Controls werden seit V2 aus einer kompakten Tabelle beschrieben.
Ein Descriptor enthielt zunächst 13 Bytes mit:

  - Style-Index / Font-Flag
  - Textoffset
  - x / y
  - Breite / Höhe
  - Control-ID

Gemeinsame Tabellen liefern Systemklasse und Window-Style.
Die erzeugten HWNDs werden fortlaufend in dem passend angeordneten Handle-
Bereich hTitle ... hList gespeichert.

Sonderinitialisierung blieb außerhalb des Builders:

  - Positionsregler: 0 .. 1000
  - Lautstärkeregler: 0 .. 100, Startwert 30
  - Playlist laden
  - GUI-Timer starten

MCI-, Playlist-, Seek- und Owner-Draw-Logik wurden dabei nicht verändert.

Ergebnis V2:
11.776 Bytes

Gewinn gegenüber V1:
1.024 Bytes

Gesamtgewinn gegenüber Original:
14.848 Bytes


3. V3 / V3.1 – Feinoptimierung und Umbenennung
-----------------------------------------------

Ab dieser Runde wurde das Programm von:

  MiniAmp 0.6e

in:

  CBR-MP3Amp 0.6e

umbenannt.

Neben sichtbaren Fenstertiteln und Fehlermeldungen wurde auch der interne
Win32-Klassenname angepasst.


Kompaktere GUI-Deskriptoren
----------------------------

Die V2-Control-Deskriptoren wurden von 13 auf 11 Bytes verkleinert.

Neu:

  db Style/Font-Flag
  db Textoffset
  dw x, y, Breite, Höhe
  db Control-ID

Damit werden Textoffset und Control-ID nur noch als Byte gespeichert.

Auch gui_class_offsets wurde von WORD- auf BYTE-Einträge verkleinert.


Initialtexte nicht doppelt speichern
------------------------------------

Die statischen Starttexte für:

  "Lautstaerke: 30%"
  "00:00 / 00:00"

werden nicht mehr als eigene fertige Strings gespeichert.

Nach dem Erzeugen der Controls werden stattdessen die ohnehin vorhandenen
Routinen aufgerufen:

  UpdateVolumeLabel
  UpdateTimeText

So entsteht derselbe sichtbare Anfangszustand aus bereits vorhandenem Code.


Gemeinsamer MCI-Helfer
----------------------

Viele MCI-Befehle verwendeten immer dieselbe Form:

  mciSendStringW(command, 0, 0, 0)

Dafür wurde ein gemeinsamer Helper eingeführt:

  MciCmd0

Er setzt die drei nicht benötigten Argumente auf Null und ruft
mciSendStringW auf.

Verwendet wird er unter anderem für:

  resume song
  pause song
  stop song
  close song
  open ...
  set song time format milliseconds
  setaudio song volume to ...
  seek song to ...
  play song

Dadurch entfällt an vielen Stellen wiederholter Argument-Setup-Code.


Weitere V3-Bereinigungen
------------------------

  - UpdateWindow-Aufruf entfernt
  - dazugehörigen UpdateWindow-Import entfernt
  - hFind aus initialisierten Daten nach .bss verschoben
  - newVolume aus initialisierten Daten nach .bss verschoben
  - unnötiges Zurücksetzen von hFind auf INVALID_HANDLE_VALUE nach FindClose
    entfernt


4. Warum es V3.1 gibt
---------------------

Die erste V3-Fassung verwendete bereits 8-Bit-Textoffsets in den
GUI-Deskriptoren.

FASM meldete beim Assemblieren:

  Error: value out of range.
  db textPrev-classStatic

Der Grund:
Ein Byte kann nur den vorgesehenen kleinen Offset speichern, aber die kurzen
Button-Texte lagen im Datenbereich noch zu weit von classStatic entfernt.

V3.1 behebt das sauber:

  textEmpty
  textPrev
  textPlay
  textPause
  textStop
  textNext

liegen jetzt direkt in der Nähe von classStatic.

Zusätzlich gibt es eine Compile-Time-Prüfung:

  if ... > 255
      err 'GUI text offset exceeds 8-bit descriptor range'
  end if

Damit kann diese Größenoptimierung bei späteren Änderungen nicht unbemerkt
wieder über ihren 8-Bit-Bereich hinauswachsen.


5. Größenentwicklung
--------------------

Original MiniAmp 0.6e:
26.624 Bytes

V1:
12.800 Bytes

V2:
11.776 Bytes

V3:
nicht als Endgröße gewertet, da die erste Fassung wegen des zu großen
8-Bit-Textoffsets nicht assemblierte.

V3.1 / CBR-MP3Amp 0.6e:
10.752 Bytes

Gesamtersparnis:
15.872 Bytes

Reduktion:
ca. 59.6 %


6. Was funktional erhalten blieb
--------------------------------

Nach der finalen V3.1 wurde lokal bestätigt, dass weiterhin alles normal
funktioniert.

Die Größenoptimierung änderte insbesondere nicht das grundlegende
Wiedergabekonzept:

  - MCI-Wiedergabe über mciSendStringW
  - Play / Pause / Stop
  - Previous / Next
  - Positionsanzeige
  - Seek
  - Lautstärke
  - Playlist
  - Timer-Aktualisierung
  - Owner-Draw-Transportbuttons


7. Kurz zusammengefasst
-----------------------

Der größte Gewinn kam nicht durch exotische PE-Tricks, sondern durch normale
strukturelle Optimierung:

  1. Nullinitialisierte Daten nach .bss
  2. tote Variablen und Imports entfernen
  3. wiederholte GUI-Erzeugung durch eine Tabelle ersetzen
  4. Deskriptoren enger packen
  5. vorhandene Formatierungsroutinen statt doppelter Startstrings nutzen
  6. wiederholte MCI-Aufrufsequenzen über einen gemeinsamen Helper führen

Ergebnis:

  26.624 Bytes -> 10.752 Bytes

ohne EXE-Packer und mit weiterhin normal funktionierender Oberfläche und
Wiedergabe.
